from typing import Optional, List, Dict, Any
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_, desc, asc, func
from datetime import datetime, timedelta
import logging

from app.domains.models import User, UserSession, WorkOrder
from app.domains.user.schemas import UserCreate, UserUpdate, UserListQuery
from app.core.security.auth import get_password_hash, verify_password, auth_manager
from app.shared.exceptions.base import (
    NotFoundException, ConflictException, ValidationException, 
    AuthenticationException, BusinessException
)
from app.shared.constants.status_codes import BusinessCode
from app.shared.utils.datetime import utc_now
from app.shared.utils.formatter import generate_uuid

logger = logging.getLogger(__name__)


class UserRepository:
    """用户数据访问层"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def get_by_id(self, user_id: int) -> Optional[User]:
        """根据ID获取用户"""
        return self.db.query(User).filter(User.id == user_id).first()
    
    def get_by_username(self, username: str) -> Optional[User]:
        """根据用户名获取用户"""
        return self.db.query(User).filter(User.username == username).first()
    
    def get_by_email(self, email: str) -> Optional[User]:
        """根据邮箱获取用户"""
        return self.db.query(User).filter(User.email == email).first()
    
    def get_by_phone(self, phone: str) -> Optional[User]:
        """根据手机号获取用户"""
        return self.db.query(User).filter(User.phone == phone).first()
    
    def create(self, user_data: UserCreate) -> User:
        """创建用户"""
        user = User(
            username=user_data.username,
            email=user_data.email,
            phone=user_data.phone,
            password_hash=get_password_hash(user_data.password),
            full_name=user_data.full_name,
            role=user_data.role,
            is_active=user_data.is_active,
            created_at=utc_now(),
            updated_at=utc_now()
        )
        
        self.db.add(user)
        self.db.commit()
        self.db.refresh(user)
        
        logger.info(f"✅ 用户创建成功: {user.username} (ID: {user.id})")
        return user
    
    def update(self, user: User, user_data: UserUpdate) -> User:
        """更新用户信息"""
        update_data = user_data.dict(exclude_unset=True)
        
        for field, value in update_data.items():
            setattr(user, field, value)
        
        user.updated_at = utc_now()
        
        self.db.commit()
        self.db.refresh(user)
        
        logger.info(f"✅ 用户信息更新成功: {user.username}")
        return user
    
    def change_password(self, user: User, new_password: str) -> User:
        """修改密码"""
        user.password_hash = get_password_hash(new_password)
        user.updated_at = utc_now()
        
        self.db.commit()
        self.db.refresh(user)
        
        logger.info(f"✅ 用户密码修改成功: {user.username}")
        return user
    
    def update_last_login(self, user: User) -> User:
        """更新最后登录时间"""
        user.last_login_at = utc_now()
        user.updated_at = utc_now()
        
        self.db.commit()
        self.db.refresh(user)
        
        return user
    
    def get_list(self, query: UserListQuery) -> tuple[List[User], int]:
        """获取用户列表"""
        # 构建查询
        q = self.db.query(User)
        
        # 搜索条件
        if query.search:
            search_term = f"%{query.search}%"
            q = q.filter(or_(
                User.username.like(search_term),
                User.full_name.like(search_term),
                User.email.like(search_term),
                User.phone.like(search_term)
            ))
        
        # 角色筛选
        if query.role:
            q = q.filter(User.role == query.role)
        
        # 激活状态筛选
        if query.is_active is not None:
            q = q.filter(User.is_active == query.is_active)
        
        # 排序
        sort_column = getattr(User, query.sort_by, User.created_at)
        if query.sort_order == "desc":
            q = q.order_by(desc(sort_column))
        else:
            q = q.order_by(asc(sort_column))
        
        # 获取总数
        total = q.count()
        
        # 分页
        offset = (query.page - 1) * query.page_size
        users = q.offset(offset).limit(query.page_size).all()
        
        return users, total
    
    def delete(self, user: User) -> bool:
        """删除用户"""
        try:
            self.db.delete(user)
            self.db.commit()
            
            logger.info(f"✅ 用户删除成功: {user.username}")
            return True
        except Exception as e:
            logger.error(f"❌ 用户删除失败: {e}")
            self.db.rollback()
            return False
    
    def get_user_statistics(self, user_id: int) -> Dict[str, Any]:
        """获取用户统计信息"""
        # 工单统计
        work_order_stats = self.db.query(
            WorkOrder.status,
            func.count(WorkOrder.id).label('count')
        ).filter(
            or_(
                WorkOrder.creator_id == user_id,
                WorkOrder.assignee_id == user_id
            )
        ).group_by(WorkOrder.status).all()
        
        stats = {
            'work_orders': {
                'created': self.db.query(WorkOrder).filter(WorkOrder.creator_id == user_id).count(),
                'assigned': self.db.query(WorkOrder).filter(WorkOrder.assignee_id == user_id).count(),
                'by_status': {stat.status.value: stat.count for stat in work_order_stats}
            }
        }
        
        return stats


class UserService:
    """用户业务逻辑层"""
    
    def __init__(self, db: Session):
        self.db = db
        self.repository = UserRepository(db)
    
    def authenticate(self, username: str, password: str) -> Optional[User]:
        """用户认证"""
        user = self.repository.get_by_username(username)
        
        if not user:
            logger.warning(f"⚠️  登录失败: 用户不存在 - {username}")
            return None
        
        if not user.is_active:
            logger.warning(f"⚠️  登录失败: 用户已禁用 - {username}")
            raise BusinessException(
                BusinessCode.USER_DISABLED,
                "用户账号已被禁用，请联系管理员"
            )
        
        if not verify_password(password, user.password_hash):
            logger.warning(f"⚠️  登录失败: 密码错误 - {username}")
            return None
        
        # 更新最后登录时间
        self.repository.update_last_login(user)
        
        logger.info(f"✅ 用户登录成功: {username}")
        return user
    
    def create_user(self, user_data: UserCreate) -> User:
        """创建用户"""
        # 检查用户名是否已存在
        if self.repository.get_by_username(user_data.username):
            raise ConflictException("用户名已存在")
        
        # 检查邮箱是否已存在
        if user_data.email and self.repository.get_by_email(user_data.email):
            raise ConflictException("邮箱已存在")
        
        # 检查手机号是否已存在
        if user_data.phone and self.repository.get_by_phone(user_data.phone):
            raise ConflictException("手机号已存在")
        
        return self.repository.create(user_data)
    
    def get_user_by_id(self, user_id: int) -> User:
        """根据ID获取用户"""
        user = self.repository.get_by_id(user_id)
        if not user:
            raise NotFoundException("用户", user_id)
        return user
    
    def get_user_by_username(self, username: str) -> User:
        """根据用户名获取用户"""
        user = self.repository.get_by_username(username)
        if not user:
            raise NotFoundException("用户")
        return user
    
    def update_user(self, user_id: int, user_data: UserUpdate) -> User:
        """更新用户信息"""
        user = self.get_user_by_id(user_id)
        
        # 检查邮箱是否被其他用户使用
        if user_data.email and user_data.email != user.email:
            existing_user = self.repository.get_by_email(user_data.email)
            if existing_user and existing_user.id != user_id:
                raise ConflictException("邮箱已存在")
        
        # 检查手机号是否被其他用户使用
        if user_data.phone and user_data.phone != user.phone:
            existing_user = self.repository.get_by_phone(user_data.phone)
            if existing_user and existing_user.id != user_id:
                raise ConflictException("手机号已存在")
        
        return self.repository.update(user, user_data)
    
    def change_password(self, user_id: int, old_password: str, new_password: str) -> bool:
        """修改密码"""
        user = self.get_user_by_id(user_id)
        
        # 验证旧密码
        if not verify_password(old_password, user.password_hash):
            raise ValidationException("旧密码不正确")
        
        # 检查新旧密码是否相同
        if verify_password(new_password, user.password_hash):
            raise ValidationException("新密码不能与旧密码相同")
        
        self.repository.change_password(user, new_password)
        
        # 使所有会话失效（强制重新登录）
        self._invalidate_user_sessions(user_id)
        
        return True
    
    def create_user_session(self, user: User, device_info: dict = None, ip_address: str = None, user_agent: str = None) -> dict:
        """创建用户会话"""
        # 创建令牌
        token_data = {
            "sub": str(user.id),
            "username": user.username,
            "role": user.role.value,
            "full_name": user.full_name
        }
        
        access_token = auth_manager.create_access_token(token_data)
        refresh_token = auth_manager.create_refresh_token(token_data)
        
        # 保存会话信息
        session = UserSession(
            user_id=user.id,
            session_token=access_token,
            refresh_token=refresh_token,
            device_info=device_info,
            ip_address=ip_address,
            user_agent=user_agent,
            expires_at=utc_now() + timedelta(minutes=auth_manager.access_token_expire_minutes),
            created_at=utc_now()
        )
        
        self.db.add(session)
        self.db.commit()
        
        logger.info(f"✅ 用户会话创建成功: {user.username}")
        
        return {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "bearer",
            "expires_in": auth_manager.access_token_expire_minutes * 60
        }
    
    def refresh_user_session(self, refresh_token: str) -> dict:
        """刷新用户会话"""
        try:
            # 验证刷新令牌
            payload = auth_manager.decode_token(refresh_token)
            
            if payload.get("type") != "refresh":
                raise AuthenticationException("无效的刷新令牌")
            
            user_id = int(payload.get("sub"))
            user = self.get_user_by_id(user_id)
            
            if not user.is_active:
                raise BusinessException(BusinessCode.USER_DISABLED)
            
            # 查找会话记录
            session = self.db.query(UserSession).filter(
                UserSession.refresh_token == refresh_token,
                UserSession.is_active == True
            ).first()
            
            if not session:
                raise AuthenticationException("会话不存在或已失效")
            
            # 创建新的令牌
            token_data = {
                "sub": str(user.id),
                "username": user.username,
                "role": user.role.value,
                "full_name": user.full_name
            }
            
            new_access_token = auth_manager.create_access_token(token_data)
            new_refresh_token = auth_manager.create_refresh_token(token_data)
            
            # 更新会话信息
            session.session_token = new_access_token
            session.refresh_token = new_refresh_token
            session.expires_at = utc_now() + timedelta(minutes=auth_manager.access_token_expire_minutes)
            session.updated_at = utc_now()
            
            self.db.commit()
            
            logger.info(f"✅ 用户会话刷新成功: {user.username}")
            
            return {
                "access_token": new_access_token,
                "refresh_token": new_refresh_token,
                "token_type": "bearer",
                "expires_in": auth_manager.access_token_expire_minutes * 60
            }
            
        except Exception as e:
            logger.error(f"❌ 刷新会话失败: {e}")
            raise AuthenticationException("会话刷新失败")
    
    def logout_user(self, user_id: int, access_token: str = None):
        """用户登出"""
        # 使指定会话失效
        if access_token:
            session = self.db.query(UserSession).filter(
                UserSession.user_id == user_id,
                UserSession.session_token == access_token,
                UserSession.is_active == True
            ).first()
            
            if session:
                session.is_active = False
                session.updated_at = utc_now()
                self.db.commit()
        
        logger.info(f"✅ 用户登出成功: 用户ID {user_id}")
    
    def _invalidate_user_sessions(self, user_id: int):
        """使用户所有会话失效"""
        self.db.query(UserSession).filter(
            UserSession.user_id == user_id,
            UserSession.is_active == True
        ).update({
            "is_active": False,
            "updated_at": utc_now()
        })
        
        self.db.commit()
        logger.info(f"✅ 用户所有会话已失效: 用户ID {user_id}")
    
    def get_user_list(self, query: UserListQuery) -> tuple[List[User], int]:
        """获取用户列表"""
        return self.repository.get_list(query)
    
    def get_user_profile(self, user_id: int) -> Dict[str, Any]:
        """获取用户档案"""
        user = self.get_user_by_id(user_id)
        stats = self.repository.get_user_statistics(user_id)
        
        return {
            "user": user,
            "statistics": stats
        }
    
    def delete_user(self, user_id: int) -> bool:
        """删除用户"""
        user = self.get_user_by_id(user_id)
        
        # 检查是否可以删除（例如是否有关联的工单等）
        work_order_count = self.db.query(WorkOrder).filter(
            or_(
                WorkOrder.creator_id == user_id,
                WorkOrder.assignee_id == user_id
            )
        ).count()
        
        if work_order_count > 0:
            raise BusinessException(
                BusinessCode.OPERATION_FAILED,
                f"无法删除用户，存在 {work_order_count} 个关联的工单"
            )
        
        return self.repository.delete(user)